<?php

namespace app\common\services;

use app\common\model\Type;

class TypeConsistent
{
    /**
     * @var Type[]
     */
    protected $types = null;

    /**
     * @var Type[]
     */
    protected $rootTypeList = null;

    /**
     * @var TypeNode
     */
    protected $typeTree = null;

    /**
     * @var int
     */
    protected $nodeCount = null;

    public function __construct($types)
    {
        $this->types = [];
        foreach ($types as $type) {
            $this->types[] = $type;
        }
    }

    /**
     * @return Type[]
     */
    public function getRootTypeList()
    {
        if ($this->rootTypeList === null) {
            $this->rootTypeList = array_values(array_filter(
                $this->types,
                function ($item) {
                    /** @var Type $item */
                    return $item->parent_type_id === 0;
                }
            ));
        }
        return $this->rootTypeList;
    }

    public function getRootTypeOutputCodeList()
    {
        $rootTypeList = $this->getRootTypeList();
        return array_map(
            function ($item) {
                /** @var Type $item */
                return $item->output_code;
            },
            $rootTypeList
        );
    }

    public function isRootTypeOutputCodeValid()
    {
        $rootTypeOutputCodeList = $this->getRootTypeOutputCodeList();
        sort($rootTypeOutputCodeList);
        $n = count($rootTypeOutputCodeList);
        for ($i = 0; $i < $n; $i++) {
            if ($rootTypeOutputCodeList[$i] !== $i) {
                return false;
            }
        }
        return true;
    }

    public function isParentExist()
    {
        foreach ($this->types as $type) {
            if ($type->parent_type_id !== 0) {
                $parents = array_values(array_filter($this->types, function ($item) use ($type) {
                    /** @var Type $item */
                    return $item->id === $type->parent_type_id;
                }));
                if (\count($parents) !== 1) {
                    return false;
                }
            }
        }
        return true;
    }

    public function getTypeTree()
    {
        if ($this->typeTree === null) {
            $types = array_values($this->types);
            $this->typeTree = new TypeNode($types, null);
        }
        return $this->typeTree;
    }

    public function getTreeHeight()
    {
        return $this->getTypeTree()->height;
    }

    public function getNodeCount()
    {
        if ($this->nodeCount === null) {
            $tree = $this->getTypeTree();
            $this->nodeCount = $this->countTree($tree) - 1;
        }
        return $this->nodeCount;
    }

    public function hasUselessNode()
    {
        $nodeCount = $this->getNodeCount();
        return count($this->types) !== $nodeCount;
    }

    public function countTree(TypeNode $node)
    {
        $n = 1;
        foreach ($node->children as $item) {
            $n += $this->countTree($item);
        }
        return $n;
    }

    public function isTreeValid()
    {
        return $this->getTypeTree()->consistent;
    }

    public function checkConsistent($max_level = 2)
    {
        if (!$this->isRootTypeOutputCodeValid()) {
            $n = count($this->getRootTypeOutputCodeList());
            return [
                'result' => false,
                'valid' => false,
                'message' => '题目类型输出编号错误，必须是0~' . ($n - 1),
            ];
        }

        if (!$this->isParentExist()) {
            return [
                'result' => false,
                'valid' => false,
                'message' => '题目类型不能构成树，存在无父节点的节点',
            ];
        }

        if ($this->hasUselessNode()) {
            return [
                'result' => false,
                'valid' => false,
                'message' => '题目类型不能构成树，存在无用节点（有环图）',
            ];
        }

        if ($this->getTreeHeight() > $max_level) {
            return [
                'result' => false,
                'valid' => false,
                'message' => '题目类型树高度超过限定高度：' . $max_level,
            ];
        }

        return [
            'result' => true,
            'valid' => $this->isTreeValid(),
            'message' => $this->isTreeValid() ? '题目类型树状态有效' : '题目类型树一致性冲突，但可修复',
        ];
    }

    public function fixConsistent()
    {
        // 从根节点开始修复
        $this->getTypeTree()->freshData();
    }
}
